#include "StdAfx.h"
#include "App.h"

App::App(void)
{
	pD3D_ = nullptr;
	device_ = nullptr;
	vertexDeclaration_ = nullptr;
	vertexBuffer_ = nullptr;
	indexBuffer_ = nullptr;
	shader_ = nullptr;
	worldViewProjHandle_ = nullptr;
}

App::~App(void)
{
}

bool App::Initialize(HWND windowHandle)
{
	CoInitialize(nullptr);
	CreateDevice(windowHandle);
	return true;
}

bool App::Dispose(void)
{
	if (texture_ != nullptr)
	{
		texture_->Release();
		texture_ = nullptr;
	}

	if (detailTexture_ != nullptr)
	{
		detailTexture_->Release();
		detailTexture_ = nullptr;
	}

	if (shader_ != nullptr)
	{
		shader_->Release();
		shader_ = nullptr;
	}

	if (indexBuffer_ != nullptr)
	{
		indexBuffer_->Release();
		indexBuffer_ = nullptr;
	}

	if (vertexBuffer_ != nullptr)
	{
		vertexBuffer_->Release();
		vertexBuffer_ = nullptr;
	}

	if (vertexDeclaration_ != nullptr)
	{
		vertexDeclaration_->Release();
		vertexDeclaration_ = nullptr;
	}

	if (device_ != nullptr)
	{
		device_->Release();
		device_ = nullptr;
	}

	if (pD3D_ != nullptr)
	{
		pD3D_->Release();
		pD3D_ = nullptr;
	}

	CoUninitialize();
	return true;
}

bool App::CreateDevice(HWND windowHandle)
{
	pD3D_ = Direct3DCreate9(D3D_SDK_VERSION);
	if (pD3D_ == nullptr)
		return false;

	D3DDISPLAYMODE displayMode;
	pD3D_->GetAdapterDisplayMode (D3DADAPTER_DEFAULT, &displayMode);

	D3DPRESENT_PARAMETERS pp;
	pp.Windowed = true;
	pp.SwapEffect = D3DSWAPEFFECT_DISCARD;
	pp.BackBufferWidth = 1280;
	pp.BackBufferHeight = 720;
	pp.BackBufferFormat =  displayMode.Format;
	pp.BackBufferCount = 1;
	pp.MultiSampleType = D3DMULTISAMPLE_NONE;
	pp.MultiSampleQuality = 0;
	pp.hDeviceWindow = windowHandle;
	pp.EnableAutoDepthStencil = true;
	pp.AutoDepthStencilFormat = D3DFMT_D24S8;
	pp.Flags = 0;
	pp.FullScreen_RefreshRateInHz = 0;
	pp.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;

	HRESULT createDeviceResult =
		pD3D_->CreateDevice(0, D3DDEVTYPE_HAL, windowHandle, D3DCREATE_HARDWARE_VERTEXPROCESSING, &pp, &device_);

	if (createDeviceResult != D3D_OK)
		return false;

	return true;
}

bool App::CreateVertexDeclaration(void)
{
	D3DVERTEXELEMENT9 vertexElements[] = {
		{0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
		{0, 12, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0},
		D3DDECL_END()
	};

	vertexDeclaration_ = nullptr;
	HRESULT result = device_->CreateVertexDeclaration(vertexElements, &vertexDeclaration_);

	return SUCCEEDED(result);
}

bool App::CreateVertexBuffer(UINT vertexCount)
{
	HRESULT result = device_->CreateVertexBuffer(vertexCount * sizeof(Vertex), 0, 0, D3DPOOL_DEFAULT, &vertexBuffer_, nullptr);

	return SUCCEEDED(result);
}

bool App::SetVertexBuffer(UINT vertexCount, Vertex vertices[])
{
	// Lock a part of the vertex buffer to write data
	void* vertexBufferData = nullptr;
	HRESULT result = vertexBuffer_->Lock(0, vertexCount * sizeof(Vertex), &vertexBufferData, 0);
	if (FAILED(result))
		return false;

	// Copy our vertices to the vertex buffer locked data
	memcpy(vertexBufferData, vertices, vertexCount * sizeof(Vertex));

	// Unlock the vertex buffer
	vertexBuffer_->Unlock();

	return true;
}

bool App::CreateIndexBuffer(unsigned int indexCount)
{
	HRESULT result
		= device_->CreateIndexBuffer
			(indexCount * sizeof(short), D3DUSAGE_WRITEONLY, D3DFMT_INDEX16, D3DPOOL_DEFAULT, &indexBuffer_, nullptr);
	return SUCCEEDED(result);
}

bool App::SetIndexBuffer(unsigned int indexCount, short indices[])
{
	// Lock a part of the index buffer to write data
	void* data = nullptr;
	HRESULT result = indexBuffer_->Lock(0, indexCount * sizeof(short), &data, 0);
	if (FAILED(result))
		return false;

	// Copy our vertices to the vertex buffer locked data
	memcpy(data, indices, indexCount * sizeof(short));

	// Unlock the vertex buffer
	indexBuffer_->Unlock();

	return true;
}

bool App::CreateShader(void)
{
	LPD3DXBUFFER compilationErrors = nullptr;
	HRESULT result = D3DXCreateEffectFromFile(device_, L"shader.fx", nullptr, nullptr, 0, nullptr, &shader_, &compilationErrors);
	
	if (FAILED(result))
	{
		if (compilationErrors == nullptr)
			MessageBox(nullptr, L"Error during CreateShader. No compilation errors. File not found ?", L"Error", 0);
		else
		{
			MessageBoxA(NULL, (char *) compilationErrors->GetBufferPointer(), "Error", 0);
			compilationErrors->Release();
		}
		return false;
	}

	worldViewProjHandle_ = shader_->GetParameterByName(nullptr, "WorldViewProj");
	return CreateTexture();
}

bool App::CreateTexture(void)
{
	D3DXHANDLE textureHandle = nullptr;
	HRESULT result = D3DXCreateTextureFromFile(device_, L"terraintexture.jpg", &texture_);
	if (FAILED(result))
		return false;
	textureHandle = shader_->GetParameterByName(nullptr, "Texture");
	shader_->SetTexture(textureHandle, texture_);

	result = D3DXCreateTextureFromFile(device_, L"detail.jpg", &detailTexture_);
	if (FAILED(result))
		return false;
	textureHandle = shader_->GetParameterByName(nullptr, "Detail");
	shader_->SetTexture(textureHandle, detailTexture_);

	return true;
}

void App::InitMatrices(void)
{
	D3DXMatrixPerspectiveFovLH(&proj_, 1.57f, 1280.0f / 720.0f, 1, 1000);

	eye = D3DXVECTOR3(0.0f, 100.0f, 0.0f);
	lookAt = D3DXVECTOR3(100.0f, 0.0f, 100.0f);
	up = D3DXVECTOR3(0.0f, 1.0f, 0.0f);
	D3DXMatrixLookAtLH(&view_, &eye, &lookAt, &up);

	D3DXMatrixIdentity(&world_);
}

void App::RenderFrame(D3DPRIMITIVETYPE primitiveType, UINT vertexCount, UINT primitiveCount)
{
	D3DXMatrixLookAtLH(&view_, &eye, &lookAt, &up);

	worldViewProj_ = world_ * view_ * proj_;
	shader_->SetMatrix(worldViewProjHandle_, &worldViewProj_);

	device_->Clear(0, nullptr, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL, D3DCOLOR_ARGB(0, 0, 0, 0), 1.0f, 0);
	device_->BeginScene();

	unsigned int passCount;
	shader_->Begin(&passCount, 0);
	for (unsigned int passIndex = 0; passIndex < passCount; ++passIndex)
	{
		shader_->BeginPass(passIndex);
		//pEffect->CommitChanges(); // TODO: What does CommitChanges do ?
		device_->SetVertexDeclaration(vertexDeclaration_);
		device_->SetIndices(indexBuffer_);
		device_->SetStreamSource(0, vertexBuffer_, 0, sizeof(Vertex));
		//device_->DrawPrimitive(primitiveType, startVertex, primitiveCount);
		device_->DrawIndexedPrimitive(primitiveType, 0, 0, vertexCount, 0, primitiveCount);
		shader_->EndPass();
	}
	shader_->End();
			
	device_->EndScene();
	device_->Present(nullptr, nullptr, nullptr, nullptr);
}